# -*- coding: utf-8 -*-
"""
智慧职教MOOC视频下载器 - 课程处理模块
负责单个课程的视频处理逻辑，包括视频发现、下载等
"""

import os
import re
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
from config import VIDEOS_DIR, MAX_CONSECUTIVE_FAILURES, COMPLETION_RATE_THRESHOLD, MOOC_INDEX_URL
from video_downloader import VideoDownloader


class CourseProcessor:
    def __init__(self, driver, main_window):
        self.driver = driver
        self.main_window = main_window
        self.video_downloader = VideoDownloader(driver)
        self.consecutive_failures = 0
        
    def process_course(self, course_info):
        """处理单个课程"""
        idx = course_info['idx']
        course_name = course_info['course_name']
        class_name = course_info['class_name']
        teacher = course_info['teacher']
        
        print(f"\n==== 正在处理第{idx}门课程: {course_name} ====")
        safe_course_name = re.sub(r'[\\/:*?\"<>|]', '_', course_name)
        course_dir = os.path.join(VIDEOS_DIR, safe_course_name)
        
        try:
            # 检查driver连接
            self.driver.title
        except Exception as e:
            print(f"driver失联: {e}")
            return False
        
        try:
            # 回到主窗口并查找课程
            self.driver.switch_to.window(self.driver.window_handles[0])
            WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.CLASS_NAME, "class-list")))
            
            target_course = self._find_target_course(course_name)
            if not target_course:
                return False
            
            # 进入课程
            if not self._enter_course(target_course):
                return False
            
            # 获取视频节点
            video_points = self.driver.find_elements(By.XPATH, "//div[@id='learnMenu']//div[@itemtype='video' and contains(@class, 's_point')]")
            total_videos = len(video_points)
            print(f"共找到{total_videos}个视频节点")
            
            if total_videos == 0:
                print("未找到视频节点，跳过该课程")
                return True
            
            # 创建课程目录
            os.makedirs(course_dir, exist_ok=True)
            
            # 分析现有文件
            existing_files = self._scan_existing_files(course_dir)
            existing_count = len(existing_files)
            completion_rate = existing_count / total_videos if total_videos > 0 else 0
            
            print(f"课程目录中已存在 {existing_count} 个有效视频文件")
            print(f"完整度: {completion_rate:.1%} ({existing_count}/{total_videos})")
            
            # 判断是否需要处理
            if existing_count >= total_videos and existing_count > 0:
                print(f"✅ 课程视频已完整，跳过整个课程")
                return True
            
            # 确定要处理的视频
            video_to_process = self._determine_videos_to_process(
                video_points, existing_files, completion_rate, total_videos
            )
            
            if not video_to_process:
                print("✅ 所有视频已存在，跳过课程")
                return True
            
            # 处理视频
            success_count = self._process_videos(video_to_process, course_dir, course_name)
            
            print(f"课程 {course_name} 处理完毕，成功处理 {success_count} 个视频")
            return True
            
        except Exception as e:
            print(f"处理课程失败: {e}")
            return False
        finally:
            self._cleanup_windows()
    
    def _find_target_course(self, course_name):
        """查找目标课程（支持模糊匹配）"""
        course_list = self.driver.find_elements(By.CSS_SELECTOR, ".class-list .class-row")
        target_course = None
        
        # 首先尝试精确匹配
        for course in course_list:
            name = course.find_element(By.CSS_SELECTOR, ".course-name").text.strip()
            if name == course_name:
                target_course = course
                break
        
        # 如果精确匹配失败，尝试模糊匹配
        if not target_course:
            print(f"精确匹配失败，尝试模糊匹配课程: {course_name}")
            for course in course_list:
                name = course.find_element(By.CSS_SELECTOR, ".course-name").text.strip()
                if course_name in name or name in course_name:
                    print(f"找到模糊匹配: '{name}' 匹配 '{course_name}'")
                    target_course = course
                    break
        
        if not target_course:
            print(f"未找到课程（精确或模糊匹配都失败）: {course_name}")
            # 打印当前页面所有课程名称以便调试
            print("当前页面课程列表:")
            for i, course in enumerate(course_list, 1):
                try:
                    name = course.find_element(By.CSS_SELECTOR, ".course-name").text.strip()
                    print(f"  {i}. {name}")
                except:
                    print(f"  {i}. 无法获取课程名称")
        
        return target_course
    
    def _enter_course(self, target_course):
        """进入课程"""
        try:
            learn_btn = target_course.find_element(By.XPATH, ".//a[contains(text(), '课程学习')]")
            before_handles = set(self.driver.window_handles)
            learn_btn.click()
            time.sleep(3)
            
            # 处理新窗口
            after_handles = set(self.driver.window_handles)
            new_handles = after_handles - before_handles
            if new_handles:
                new_handle = new_handles.pop()
                self.driver.switch_to.window(new_handle)
            
            # 等待并切换到iframe
            try:
                WebDriverWait(self.driver, 15).until(
                    EC.frame_to_be_available_and_switch_to_it((By.ID, "mainContent"))
                )
            except TimeoutException:
                print("iframe mainContent 加载超时")
                return False
            
            # 等待页面完全加载
            if not self._wait_for_page_ready():
                print("页面未完全加载，跳过该课程")
                return False
            
            return True
            
        except Exception as e:
            print(f"进入课程失败: {e}")
            return False
    
    def _wait_for_page_ready(self, max_wait=30):
        """等待页面完全加载和所有必要的JavaScript对象初始化"""
        print("等待页面完全加载...")
        for i in range(max_wait):
            try:
                ready_state = self.driver.execute_script("return document.readyState")
                if ready_state == "complete":
                    # 检查关键函数和对象是否存在
                    check_result = self.driver.execute_script("""
                        try {
                            if (typeof openLearnResItem !== 'function') {
                                return 'openLearnResItem函数未找到';
                            }
                            if (typeof window.jQuery === 'undefined') {
                                return 'jQuery未加载';
                            }
                            if (typeof window.$ === 'undefined') {
                                return '$对象未找到';
                            }
                            var menu = document.getElementById('learnMenu');
                            if (!menu) {
                                return '学习菜单未找到';
                            }
                            return 'ready';
                        } catch(e) {
                            return '检查异常: ' + e.message;
                        }
                    """)
                    
                    if check_result == 'ready':
                        print("✓ 页面完全加载完成")
                        return True
                    else:
                        print(f"等待中... ({i+1}/{max_wait}) - {check_result}")
                else:
                    print(f"等待页面加载... ({i+1}/{max_wait}) - {ready_state}")
                time.sleep(1)
            except Exception as e:
                print(f"检查页面状态异常: {e}")
                time.sleep(1)
        
        print("⚠️ 页面加载超时")
        return False
    
    def _scan_existing_files(self, course_dir):
        """扫描已存在的文件"""
        existing_files = []
        if os.path.exists(course_dir):
            existing_files = [
                f for f in os.listdir(course_dir) 
                if f.endswith('.mp4') and os.path.getsize(os.path.join(course_dir, f)) > 1024
            ]
        return existing_files
    
    def _determine_videos_to_process(self, video_points, existing_files, completion_rate, total_videos):
        """确定需要处理的视频"""
        if completion_rate >= COMPLETION_RATE_THRESHOLD:
            print("执行快速预检查...")
            existing_titles = set()
            for file in existing_files:
                clean_title = re.sub(r'\(\d+\)\.mp4$', '', file).replace('.mp4', '')
                existing_titles.add(clean_title)
            
            missing_videos = []
            for v_idx, vp in enumerate(video_points, 1):
                try:
                    title = self._extract_video_title(vp, v_idx)
                    safe_title = re.sub(r'[\\/:*?\"<>|]', '_', title)
                    
                    # 检查是否存在相似文件
                    found = False
                    for existing_title in existing_titles:
                        if (existing_title.startswith(safe_title[:20]) or 
                            safe_title.startswith(existing_title[:20]) or
                            f"视频{v_idx}" in existing_title):
                            found = True
                            print(f"预检查发现匹配文件: {title} -> {existing_title}")
                            break
                    
                    if not found:
                        missing_videos.append((v_idx, None, title))
                        print(f"预检查发现缺失: 第{v_idx}个视频 - {title}")
                        
                except Exception as e:
                    print(f"预检查第{v_idx}个视频失败: {e}")
                    missing_videos.append((v_idx, None, f"视频{v_idx}"))
            
            if not missing_videos:
                print(f"✅ 快速检查确认所有视频已存在")
                return []
            else:
                print(f"🎯 发现{len(missing_videos)}个缺失视频，只处理这些视频...")
                return missing_videos
        else:
            # 完整度不高，处理所有视频
            return [(v_idx, None, None) for v_idx in range(1, total_videos + 1)]
    
    def _extract_video_title(self, vp, v_idx):
        """提取视频标题"""
        title = None
        
        # 方法1: s_pointti元素的title属性
        try:
            title_element = vp.find_element(By.CLASS_NAME, 's_pointti')
            title = title_element.get_attribute('title')
            if not title or title.strip() == '':
                title = title_element.text.strip()
        except Exception:
            pass
        
        # 方法2: 任何有title属性的子元素
        if not title or title.strip() == '':
            try:
                title_elements = vp.find_elements(By.CSS_SELECTOR, '[title]')
                for elem in title_elements:
                    temp_title = elem.get_attribute('title')
                    if temp_title and temp_title.strip():
                        title = temp_title.strip()
                        break
            except Exception:
                pass
        
        # 方法3: 使用视频ID作为标识
        if not title or title.strip() == '':
            try:
                vid = vp.get_attribute('id')
                if vid:
                    title = f"视频{v_idx}_{vid}"
            except Exception:
                title = f"视频{v_idx}"
        
        if not title:
            title = f"视频{v_idx}"
        
        return title
    
    def _process_videos(self, video_to_process, course_dir, course_name):
        """处理视频列表"""
        success_count = 0
        downloaded_videos = set()
        
        for v_idx, _, preset_title in video_to_process:
            try:
                if self._process_single_video(v_idx, preset_title, course_dir, downloaded_videos):
                    success_count += 1
                    self.consecutive_failures = 0
                else:
                    self.consecutive_failures += 1
                    
                # 如果连续失败太多，尝试重新进入课程
                if self.consecutive_failures >= MAX_CONSECUTIVE_FAILURES:
                    print("⚠️ 连续多个视频失败，尝试重新进入课程...")
                    if self._re_enter_course(course_name):
                        self.consecutive_failures = 0
                    
            except Exception as e:
                print(f"❌ 处理第{v_idx}个视频失败: {e}")
                self.consecutive_failures += 1
        
        return success_count
    
    def _process_single_video(self, v_idx, preset_title, course_dir, downloaded_videos):
        """处理单个视频"""
        try:
            # 刷新页面重新获取元素
            print(f"正在处理第{v_idx}个视频，刷新页面...")
            self.driver.refresh()
            time.sleep(3)
            
            # 重新进入iframe
            WebDriverWait(self.driver, 20).until(EC.frame_to_be_available_and_switch_to_it((By.ID, "mainContent")))
            
            if not self._wait_for_page_ready():
                print(f"第{v_idx}个视频页面加载失败，跳过")
                return False
            
            # 获取视频信息
            video_info = self._get_video_info(v_idx, preset_title)
            if not video_info:
                return False
            
            title, vid, learn_item_id = video_info
            safe_title = re.sub(r'[\\/:*?\"<>|]', '_', title)
            
            # 检查重复和文件存在
            if learn_item_id in downloaded_videos:
                print(f"视频ID重复，跳过: {title} (ID: {learn_item_id})")
                return True
            
            filename = os.path.join(course_dir, f"{safe_title}.mp4")
            if os.path.exists(filename):
                print(f"文件已存在，跳过: {safe_title}.mp4")
                downloaded_videos.add(learn_item_id)
                return True
            
            # 加载和下载视频
            if not self.video_downloader.load_video_item(learn_item_id):
                print(f"❌ 视频加载失败，跳过: {title}")
                return False
            
            # 等待视频加载
            print("等待视频页面加载...")
            time.sleep(5)
            
            # 尝试播放视频
            print("尝试播放视频...")
            self.video_downloader.try_play_video()
            time.sleep(3)
            
            # 抓取视频链接
            m3u8_url, mp4_url = self.video_downloader.capture_video_links()
            
            # 下载视频
            download_success = False
            if m3u8_url:
                print(f"找到m3u8链接: {m3u8_url}")
                download_success = self.video_downloader.download_m3u8_video(m3u8_url, filename)
            elif mp4_url:
                print(f"找到mp4链接: {mp4_url}")
                download_success = self.video_downloader.download_mp4_video(mp4_url, filename)
            else:
                print(f"未找到有效的视频链接: {title}")
                print("尝试更长时间等待视频加载...")
                # 延长等待时间，可能视频还在加载
                time.sleep(10)
                m3u8_url, mp4_url = self.video_downloader.capture_video_links(max_attempts=8)
                
                if m3u8_url:
                    print(f"延迟后找到m3u8链接: {m3u8_url}")
                    download_success = self.video_downloader.download_m3u8_video(m3u8_url, filename)
                elif mp4_url:
                    print(f"延迟后找到mp4链接: {mp4_url}")
                    download_success = self.video_downloader.download_mp4_video(mp4_url, filename)
                else:
                    print(f"最终未找到视频链接: {title}")
            
            # 验证下载结果
            if download_success:
                # 检查文件是否真正存在且有合理大小
                if os.path.exists(filename):
                    file_size = os.path.getsize(filename)
                    if file_size > 10240:  # 文件大于10KB才认为是有效下载
                        downloaded_videos.add(learn_item_id)
                        print(f"✓ 下载成功: {title} ({file_size/1024:.1f}KB)")
                        return True
                    else:
                        print(f"❌ 下载文件过小: {title} ({file_size}字节)")
                        # 删除无效文件
                        try:
                            os.remove(filename)
                        except:
                            pass
                        return False
                else:
                    print(f"❌ 下载文件不存在: {title}")
                    return False
            else:
                print(f"❌ 下载失败: {title}")
                return False
                
        except Exception as e:
            print(f"处理视频异常: {e}")
            return False
    
    def _get_video_info(self, v_idx, preset_title):
        """获取视频信息"""
        for attempt in range(3):
            try:
                print(f"第{attempt+1}次尝试获取视频信息...")
                
                # 重新获取所有视频节点
                video_points = self.driver.find_elements(By.XPATH, "//div[@id='learnMenu']//div[@itemtype='video' and contains(@class, 's_point')]")
                
                if v_idx > len(video_points):
                    print(f"视频节点索引超出范围: {v_idx} > {len(video_points)}")
                    return None
                
                current_vp = video_points[v_idx-1]
                vid = current_vp.get_attribute('id')
                if not vid:
                    raise Exception("无法获取视频ID")
                
                learn_item_id = vid.replace('s_point_', '') if vid.startswith('s_point_') else vid
                
                if preset_title:
                    title = preset_title
                else:
                    title = self._extract_video_title(current_vp, v_idx)
                
                if title and vid and learn_item_id:
                    print(f"✓ 成功获取视频信息: title={title}, vid={vid}")
                    return title, vid, learn_item_id
                else:
                    raise Exception(f"获取视频信息不完整")
                    
            except Exception as e:
                print(f"第{attempt+1}次获取视频信息失败: {e}")
                if attempt < 2:
                    time.sleep(3)
        
        return None
    
    def _re_enter_course(self, course_name):
        """重新进入课程"""
        try:
            self.driver.switch_to.default_content()
            self.driver.get(MOOC_INDEX_URL)
            time.sleep(3)
            
            target_course = self._find_target_course(course_name)
            if target_course:
                return self._enter_course(target_course)
            return False
            
        except Exception as e:
            print(f"重新进入课程失败: {e}")
            return False
    
    def _cleanup_windows(self):
        """清理窗口"""
        try:
            self.driver.switch_to.default_content()
            current_handles = self.driver.window_handles
            
            for handle in current_handles:
                if handle != self.main_window:
                    try:
                        self.driver.switch_to.window(handle)
                        self.driver.close()
                    except Exception:
                        pass
            
            self.driver.switch_to.window(self.main_window)
            
        except Exception as e:
            print(f"清理窗口时出错: {e}")
            try:
                self.driver.get(MOOC_INDEX_URL)
                time.sleep(2)
            except Exception:
                pass 